#include "thread.h"
#include "stdint.h"
#include "string.h"
#include "global.h"
#include "debug.h"
#include "interrupt.h"
#include "print.h"
#include "memory.h"

#define PG_SIZE 4096

struct task_struct* main_thread;    // 主线程PCB
struct list thread_ready_list;	    // 就绪队列
struct list thread_all_list;	    // 所有任务队列
static struct list_elem* thread_tag;// 用于保存队列中的线程结点

extern void switch_to(struct task_struct* cur, struct task_struct* next);

/* 获取当前线程pcb指针 */
struct task_struct* running_thread() {
   uint32_t esp; 
   asm ("mov %%esp, %0" : "=g" (esp));
  /* 取esp整数部分即pcb起始地址 */
   return (struct task_struct*)(esp & 0xfffff000);
}

// 由 kernel_thread 去执行 function(func_arg)
static void kernel_thread(thread_func* function, void* func_arg) {
    // 在执行 function 前要先打开中断，避免后面的时钟中断被屏蔽，而无法调度其它线程
    intr_enable();
    function(func_arg);
}

// 初始化线程栈 thread_stack, 将待执行的函数和实参放到 thread_stack 中相应的位置
void thread_create(struct task_struct* pthread, thread_func function, void* func_arg) {
    // 先预留中断使用栈的空间
    pthread -> self_kstack -= sizeof(struct intr_stack);

    // 留出线程栈的空间
    pthread -> self_kstack -= sizeof(struct thread_stack);
    struct thread_stack* kthread_stack = (struct thread_stack*) pthread -> self_kstack;
    kthread_stack -> eip = kernel_thread;
    kthread_stack -> function = function;
    kthread_stack -> func_arg = func_arg;
    kthread_stack -> ebp = kthread_stack -> ebx = kthread_stack -> esi = kthread_stack -> edi = 0;
}

// 初始化线程基本信息
void init_thread(struct task_struct* pthread, char* name, int prio) {
    memset(pthread, 0, sizeof(*pthread));
    strcpy(pthread -> name, name);

    if(pthread == main_thread) 
        pthread -> status = TASK_RUNNING;
    else
        pthread -> status = TASK_READY;

    // self_kstack 是线程自己在内核态下使用的栈顶地址
    pthread -> self_kstack = (uint32_t*) ((uint32_t) pthread + PG_SIZE);
    pthread -> priority = prio;
    pthread -> ticks = prio;
    pthread -> elapsed_ticks = 0;
    pthread -> pgdir = NULL;
    pthread -> stack_magic = 0x19870916; // 自定义的魔数，用于栈边界判断
}

// 创建一个优先级为 prio 的线程，线程名为 name，线程所执行的函数是 function(func_arg)
struct task_struct* thread_start(char* name, int prio, thread_func function, void* func_arg) {
    // 申请一页内核空间（内含PCB，这是每个线程必备的）
    struct task_struct* thread = get_kernel_pages(1);

    init_thread(thread, name, prio);
    thread_create(thread, function, func_arg);

    /* 确保之前不在队列中 */
    ASSERT(!elem_find(&thread_ready_list, &thread->general_tag));
    /* 加入就绪线程队列 */
    list_append(&thread_ready_list, &thread->general_tag);
    
    /* 确保之前不在队列中 */
    ASSERT(!elem_find(&thread_all_list, &thread->all_list_tag));
    /* 加入全部线程队列 */
    list_append(&thread_all_list, &thread->all_list_tag);

    return thread;
}

// 将 kernel 中的 main 函数完善为主线程
static void make_main_thread(void) {
    // 因为 main 线程早已运行，在 loader.S 中进入内核时设置 mov esp, 0xc009f000
    // 便是为其预留 PCB，地址为 0xc009f00，因此不需要通过 get_kernel_page 另分配一页
    main_thread = running_thread();
    init_thread(main_thread, "main", 31);

    // main 函数是当前线程，当前现在不在 thread_read_list 中
    // 所以需要将其加入到 thread_all_list 队列中
    ASSERT(!elem_find(&thread_all_list, &main_thread -> all_list_tag));
    list_append(&thread_all_list, &main_thread -> all_list_tag);
}

// 任务调度
void schedule() {
    ASSERT(intr_get_status() == INTR_OFF);

    struct task_struct* cur = running_thread(); // 获取当前正在执行的线程的 PCB 地址

    if(cur -> status == TASK_RUNNING) { // 判断当前线程的时间片是否到期
        ASSERT(!elem_find(&thread_ready_list, &cur -> general_tag));
        // 那么就加入就绪队列
        list_append(&thread_ready_list, &cur -> general_tag);
        cur -> ticks = cur -> priority; // 重置时间片
        cur -> status = TASK_READY;     // 重置状态为就绪状态
    } else {
        /* 若此线程需要某事件发生后才能继续上cpu运行,
           不需要将其加入队列,因为当前线程不在就绪队列中。
        */
    }

    ASSERT(!list_empty(&thread_ready_list));
    thread_tag = NULL;
    // 弹出队列的首元素，准备将其调度上处理器上执行
    thread_tag = list_pop(&thread_ready_list);
    struct task_struct* next = elem2entry(struct task_struct, general_tag, thread_tag);
    next -> status = TASK_RUNNING;
    switch_to(cur, next);
}

/* =========================================== */
// 阻塞
// -------------------------------------------

// 将当前线程阻塞
void thread_block(enum task_status stat) {
    // state 只能取这些值
    ASSERT(((stat == TASK_BLOCKED) || (stat == TASK_WAITING) || (stat == TASK_HANGING)));

    enum intr_status old_status = intr_disable(); // 关闭中断，保证原子性
    struct task_struct* cur_thread = running_thread();
    cur_thread -> status = stat;
    schedule(); // 调度下一个线程
    intr_set_status(old_status); // 待当前线程被解除阻塞后需要恢复中断
}

// 解除某个线程的阻塞
void thread_unblock(struct task_struct* pthread) {
    enum intr_status old_status = intr_disable();
    ASSERT(((pthread -> status == TASK_BLOCKED) || (pthread -> status == TASK_WAITING) || (pthread -> status == TASK_HANGING)));
    if(pthread -> status != TASK_READY) { // 被阻塞的线程必然不会是 TASK_READY, 作者说“以防万一”
        // 作者：按理说就绪队列不会出现已阻塞的线程，下面两行仅仅为了“以防万一”
        ASSERT(!elem_find(&thread_ready_list, &pthread -> general_tag));
        if(elem_find(&thread_ready_list, &pthread -> general_tag)) {
            PANIC("thread_unblock: blocked thread in ready_list.\n");
        }
        // 将被阻塞的线程 pthread 插入就绪队列
        list_push(&thread_ready_list, &pthread -> general_tag);
        // 设置线程状态
        pthread -> status = TASK_READY;
    }
    // 恢复中断
    intr_set_status(old_status);
}

/* =========================================== */

// 初始化线程环境
void thread_init(void) {
    put_str("thread_init start\n");
    list_init(&thread_ready_list);
    list_init(&thread_all_list);
    // 将当前 main 函数创建为线程
    make_main_thread();
    put_str("thead_init done\n");
}